/*
 * Copyright (c) novafaen design 2009 
 */
package tentaflykt.editor.xml;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Stack;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import tentaflykt.SystemConstants;
import tentaflykt.editor.EditorRes;

/**
 * Editor settings, XML file parser.
 * @see tentaflykt.editor.xml.dtd.editor.dtd
 * @author Kristoffer Nilsson
 */
public class EditorSettings 
extends DefaultHandler {
  /*
   * Editor settings variables
   */
  private int windowWidth   = SystemConstants.Editor.WINDOW_WIDTH;
  private int windowHeight  = SystemConstants.Editor.WINDOW_HEIGHT;
  private int windowPosX    = SystemConstants.Editor.WINDOW_POS_LEFT;
  private int windowPosY    = SystemConstants.Editor.WINDOW_POS_TOP;
  private boolean windowMax = SystemConstants.Editor.WINDOW_MAXIMIZED;
  private int toolboxPosX   = SystemConstants.Editor.TOOLBAR_POS_LEFT;
  private int toolboxPosY   = SystemConstants.Editor.TOOLBAR_POS_TOP;
  
  /*
   * Parsing variables
   */
  private Stack<String> parseElem = new Stack<String>();
  private String attrValue = "";
  
  /*
   * XML information 
   */
  private final String ELEMENT_SETTINGS = "settings";
  private final String ELEMENT_WINDOW   = "window";
  private final String ELEMENT_TOOLBOX  = "toolbox";
  private final String ELEMENT_PROPERTY = "property";
  
  private final String ATTRIBUTE_NAME = "name";
  
  private final String VALUE_LEFT      = "left";
  private final String VALUE_TOP       = "top";
  private final String VALUE_WIDTH     = "width";
  private final String VALUE_HEIGHT    = "height";
  private final String VALUE_MAXIMIZED = "maximized";
  
  /**
   * Default constructor, creates and initiates a 
   * <code>EditorSettings</code> object.
   * @param file The 
   * @throws SAXException Validation or parsing error
   */
  public EditorSettings() {
    // nothing, just set default values
  }
  
  /**
   * Loads XML file and parse the content.
   * @throws IOException Fatal IO error
   * @throws SAXException Parse or validate error
   */
  public void loadSettings()
  throws IOException, SAXException {
    File file = new File(SystemConstants.Editor.SETTINGS_XML);
    
    // if settings file is missing, do not try to load and parse.
    if (!file.exists()) {
      return;
    }
    
    SAXParserFactory factory = SAXParserFactory.newInstance();
    
    // initiate parser to validate with DTD
    factory.setValidating(true);
    factory.setNamespaceAware(true);
    
    SAXParser parser = null;
    try {
      parser = factory.newSAXParser();
    } catch (ParserConfigurationException ex) {
      throw new SAXException(EditorRes.getString("EditorSettings.Error.Create"));
    } catch (SAXException ex) {
      throw new SAXException(EditorRes.getString("EditorSettings.Error.Create"));
    }
    
    XMLReader reader = parser.getXMLReader();
    reader.setErrorHandler(new SimpleErrorHandler());
    reader.setContentHandler(this);
    
    reader.parse(new InputSource(new FileReader(SystemConstants.Editor.SETTINGS_XML)));
  }

  /*
   * Catches start-of-document from SAX parser.
   */
  @Override
  public void startDocument() {
    // nothing
  }

  /*
   * Catches end-of-document from SAX parser.
   */
  @Override
  public void endDocument() throws SAXException {
    // nothing
  }

  /*
   * Catches start-of-element from SAX parser.
   */
  @Override
  public void startElement(String uri, String localName, String qName, Attributes attributes) 
  throws SAXException {
    parseElem.push(qName);
    
    // should only contain one attribute named "name"
    if (attributes.getLength() > 0 && 
        attributes.getLocalName(0).equalsIgnoreCase(ATTRIBUTE_NAME)) {
      attrValue = attributes.getValue(0);
    } else {
      attrValue = null;
    }
  }

  /*
   * Catches end-of-element from SAX parser.
   */
  @Override
  public void endElement(String uri, String localName, String qName)
  throws SAXException {
    parseElem.pop();
    attrValue = null;
  }

  /*
   * Catches element content from SAX parser.
   */
  @Override
  public void characters(char[] ch, int start, int length)
  throws SAXException {
    // size() - 2, since top element will be property
    String cat = parseElem.get(parseElem.size() - 2);
    String content = new String(ch, start, length);
    
    if (cat.equalsIgnoreCase(ELEMENT_TOOLBOX)) {
      /*
       * category: toolbox
       */
      if (attrValue.equalsIgnoreCase(VALUE_LEFT)) {
        try {
          toolboxPosX = Integer.parseInt(content);
        } catch (NumberFormatException ex) {
          // ignore, keep current/default position
        }
      } else if (attrValue.equalsIgnoreCase(VALUE_TOP)) {
        try {
          toolboxPosY = Integer.parseInt(content);
        } catch (NumberFormatException ex) {
          // ignore, keep current/default position
        }
      }
    } else if (cat.equalsIgnoreCase(ELEMENT_WINDOW)) {
      /*
       * category: window
       */
      if (attrValue.equalsIgnoreCase(VALUE_LEFT)) {
        try {
          windowPosX = Integer.parseInt(content);
        } catch (NumberFormatException ex) {
          // ignore, keep current/default position
        }
      } else if (attrValue.equalsIgnoreCase(VALUE_TOP)) {
        try {
          windowPosY = Integer.parseInt(content);
        } catch (NumberFormatException ex) {
          // ignore, keep current/default position
        }
      } else if (attrValue.equalsIgnoreCase(VALUE_WIDTH)) {
        try {
          windowWidth = Integer.parseInt(content);
        } catch (NumberFormatException ex) {
          // ignore, keep current/default width
        }
      } else if (attrValue.equalsIgnoreCase(VALUE_HEIGHT)) {
        try {
          windowHeight = Integer.parseInt(content);
        } catch (NumberFormatException ex) {
          // ignore, keep current/default height
        }
      } else if (attrValue.equalsIgnoreCase(VALUE_MAXIMIZED)) {
        windowMax = Boolean.parseBoolean(content);
      }
    }
  }
  
  /**
   * Saves settings to XML file on disc.
   * @throws IOException Fatal IO Exception
   */
  public void save()
  throws IOException {
    XMLFormatter formatter = 
      new XMLFormatter(
          SystemConstants.Misc.MESSAGE_GENERATED,
          ELEMENT_SETTINGS, 
          SystemConstants.Editor.SETTINGS_DTD);
    
    int tabCount = 0;
    
    formatter.startElement(ELEMENT_SETTINGS, tabCount++);
    formatter.startElement(ELEMENT_WINDOW, tabCount++);
    
    formatter.startElement(ELEMENT_PROPERTY, new XMLAttribute(ATTRIBUTE_NAME, VALUE_LEFT), tabCount);
    formatter.content(windowPosX);
    formatter.endElement(ELEMENT_PROPERTY);
    
    formatter.startElement(ELEMENT_PROPERTY, new XMLAttribute(ATTRIBUTE_NAME, VALUE_TOP), tabCount);
    formatter.content(windowPosY);
    formatter.endElement(ELEMENT_PROPERTY);
    
    formatter.startElement(ELEMENT_PROPERTY, new XMLAttribute(ATTRIBUTE_NAME, VALUE_WIDTH), tabCount);
    formatter.content(windowWidth);
    formatter.endElement(ELEMENT_PROPERTY);
    
    formatter.startElement(ELEMENT_PROPERTY, new XMLAttribute(ATTRIBUTE_NAME, VALUE_HEIGHT), tabCount);
    formatter.content(windowHeight);
    formatter.endElement(ELEMENT_PROPERTY);
    
    formatter.startElement(ELEMENT_PROPERTY, new XMLAttribute(ATTRIBUTE_NAME, VALUE_MAXIMIZED), tabCount);
    formatter.content(windowMax);
    formatter.endElement(ELEMENT_PROPERTY);
    
    formatter.endElement(ELEMENT_WINDOW, --tabCount);
    formatter.startElement(ELEMENT_TOOLBOX, tabCount++);
    
    formatter.startElement(ELEMENT_PROPERTY, new XMLAttribute(ATTRIBUTE_NAME, VALUE_LEFT), tabCount);
    formatter.content(toolboxPosX);
    formatter.endElement(ELEMENT_PROPERTY);
    
    formatter.startElement(ELEMENT_PROPERTY, new XMLAttribute(ATTRIBUTE_NAME, VALUE_TOP), tabCount);
    formatter.content(toolboxPosY);
    formatter.endElement(ELEMENT_PROPERTY);
    
    formatter.endElement(ELEMENT_TOOLBOX, --tabCount);
    formatter.endElement(ELEMENT_SETTINGS, --tabCount);
    
    try {
      BufferedWriter out = new BufferedWriter(
          new FileWriter(SystemConstants.Editor.SETTINGS_XML));
          
      out.write(formatter.getXML());
      out.close();
    } catch (IOException ex) {
      throw new IOException(ex.getMessage());
    }
  }
  
  /**
   * @return Window Width
   */
  public int getWindowWidth() {
    return windowWidth;
  }

  /**
   * @param width Window width
   */
  public void setWindowWidth(int width) {
    this.windowWidth = width;
  }

  /**
   * @return Window Height
   */
  public int getWindowHeight() {
    return windowHeight;
  }

  /**
   * @param height windowHeight
   */
  public void setWindowHeight(int height) {
    this.windowHeight = height;
  }

  /**
   * @return Window horizontal position
   */
  public int getWindowPosX() {
    return windowPosX;
  }

  /**
   * @param x Window horizontal position
   */
  public void setWindowPosX(int x) {
    this.windowPosX = x;
  }

  /**
   * @return Window vertical position
   */
  public int getWindowPosY() {
    return windowPosY;
  }

  /**
   * @param y Window vertical position
   */
  public void setWindowPosY(int y) {
    this.windowPosY = y;
  }
  
  /**
   * @return Window maximized
   */
  public boolean getWindowMaximized() {
    return windowMax;
  }
  
  /**
   * @param maximized Window maximized
   */
  public void setWindowMaximized(boolean maximized) {
    this.windowMax = maximized;
  }
  
  /**
   * @return Toolbox horizontal position
   */
  public int getToolboxPosX() {
    return toolboxPosX;
  }
  
  /**
   * @param x Toolbox horizontal position
   */
  public void setToolboxPosX(int x) {
    this.toolboxPosX = x;
  }
  
  /**
   * @return Toolbox vertical position
   */
  public int getToolboxPosY() {
    return toolboxPosY;
  }
  
  /**
   * @param y Toolbox vertical position
   */
  public void setToolboxPosY(int y) {
    this.toolboxPosY = y;
  }
}
